﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;

using CommunityServer.ControlPanel.Controls;

namespace CSLive.ControlPanel.Editor
{
    /// <summary>
    /// The Editor Page Code Behind Source Code
    /// </summary>
    public class Editor : Page
    {
        protected ResourceButton CancelButton = new ResourceButton();
        protected ResourceButton CloseButton = new ResourceButton();
        protected Label edit1 = new Label();
        protected PlaceHolder edit2 = new PlaceHolder();
        protected HiddenField editorchanged = new HiddenField();
        protected Label error = new Label();
        protected Repeater linerepeater = new Repeater();
        protected PlaceHolder lines = new PlaceHolder();
        protected Repeater livecontrols = new Repeater();
        protected PlaceHolder p1 = new PlaceHolder();
        protected ResourceButton SaveButton = new ResourceButton();
        protected PlaceHolder Saved = new PlaceHolder();
        protected HiddenField selectedfile = new HiddenField();
        protected TreeView themepages = new TreeView();
        protected PlaceHolder wait = new PlaceHolder();

        protected void Page_Load( object sender, EventArgs e )
        {
            themepages.SelectedNodeChanged += themepages_SelectedNodeChanged;
            livecontrols.ItemDataBound += livecontrols_ItemDataBound;
            CancelButton.Click += CancelButton_Click;
            SaveButton.Click += SaveButton_Click;

            if ( !Page.IsPostBack )
            {
                loadThemes();
                LoadControls();
            }
        }


        /// <summary>
        /// Saves the Current File
        /// </summary>
        /// <param name="sender">Usually the Save Button itself</param>
        /// <param name="e">Event Arguments related to the click of the button</param>
        private void SaveButton_Click( object sender, EventArgs e )
        {
            UpdateUI();
            var file = new FileInfo( selectedfile.Value );
            var doc = new XmlDocument();
            doc.LoadXml( editorchanged.Value );

            //create backup
            file.CopyTo( file.FullName.Replace( file.Extension, "-" + DateTime.Now.ToString( "ddMMyy-hhmmss" ) + ".bak" ) );
            int x = 1;

            var sb = new StringBuilder();
            using ( var sr = new StreamReader( selectedfile.Value ) )
            {
                while ( !sr.EndOfStream )
                {
                    foreach ( XmlNode node in doc.SelectSingleNode( "lines" ).ChildNodes )
                        if ( x == int.Parse( node.Attributes[ "number" ].Value ) )
                            if ( node.Name == "add" )
                            {
                                string spacing = "";
                                for ( int z = 0; z < int.Parse( node.Attributes[ "spacing" ].Value ); z++ )
                                    spacing += " ";
                                sb.AppendLine( spacing + node.InnerText );
                            }
                    if ( canAddLine( x, doc ) )
                        sb.AppendLine( sr.ReadLine() );
                    else sr.ReadLine();
                    x++;
                }
                sr.DiscardBufferedData();
                sr.Close();
                sr.Dispose();
            }
            file = null;
            using ( var sw = new StreamWriter( selectedfile.Value ) )
            {
                sw.Write( sb.ToString() );
                sw.Flush();
                sw.Close();
                sw.Dispose();
            }
            Saved.Visible = true;
            //error.Text = string.Format("<blockquote><pre>{0}</pre></blockquote>", doc.SelectSingleNode("lines").ChildNodes[0].Name);
        }

        /// <summary>
        /// Determins if it can add the current line
        /// </summary>
        /// <param name="x">The Line Number to Search For</param>
        /// <param name="doc">The XML document to get from the postback event</param>
        /// <returns>True if it can add the line, false if it can't</returns>
        private bool canAddLine( int x, XmlDocument doc )
        {
            foreach ( XmlNode node in doc.SelectSingleNode( "lines" ).ChildNodes )
                if ( x == int.Parse( node.Attributes[ "number" ].Value ) )
                    if ( node.Name == "remove" ) return false;
            return true;
        }

        /// <summary>
        /// Databinds the repeater bellow the livecontrol repeater
        /// </summary>
        /// <param name="sender">Usually the LiveControls Repeater</param>
        /// <param name="e">Repeater Item Event Arguments replated to the Item that is Being DataBound</param>
        private static void livecontrols_ItemDataBound( object sender, RepeaterItemEventArgs e )
        {
            var r = e.Item.FindControl( "controlrepeater" ) as Repeater;
            if ( r != null )
            {
                r.ItemDataBound += r_ItemDataBound;
                r.DataSource =
                    LiveConfiguration.Instance.ControlGroups[ e.Item.DataItem.ToString() ].ToArray();
                r.DataBind();
            }
        }

        /// <summary>
        /// Databinds the repeater bellow the livecontrol group
        /// </summary>
        private static void r_ItemDataBound( object sender, RepeaterItemEventArgs e )
        {
            var r = e.Item.FindControl( "templaterepeater" ) as Repeater;
            if ( r != null )
            {
                var src = e.Item.DataItem as ICSLiveControl;
                if ( src != null ) r.DataSource = src.EditorControls.Values;
                r.DataBind();
            }
        }

        /// <summary>
        /// Loads the Current Control Group and Databinds that to the LiveControls Repeater
        /// </summary>
        private void LoadControls()
        {
            livecontrols.DataSource = LiveConfiguration.Instance.ControlGroups.Keys;
            livecontrols.DataBind();
        }

        /// <summary>
        /// Clears the Current Selected Tree Node (File)
        /// </summary>
        /// <param name="node">The node to start the clear selection from</param>
        private static void clearSelection( TreeNode node )
        {
            foreach ( TreeNode n in node.ChildNodes )
            {
                n.Selected = false;
                if ( n.ChildNodes.Count > 0 ) clearSelection( n );
            }
        }

        /// <summary>
        /// Cancel's the Editing of the current page
        /// </summary>
        /// <param name="sender">Usually the Cancel Button itself</param>
        /// <param name="e">Event Argument related to the clicking on the cancel button</param>
        private void CancelButton_Click( object sender, EventArgs e )
        {
            UpdateUI();
        }

        /// <summary>
        /// Updated the UI and set's it to it's initial state
        /// </summary>
        private void UpdateUI()
        {
            foreach ( TreeNode node in themepages.Nodes )
            {
                node.Selected = false;
                if ( node.ChildNodes.Count > 0 ) clearSelection( node );
            }

            lines.Visible = false;
            wait.Visible = true;
            CancelButton.Visible = false;
            SaveButton.Visible = false;
            edit1.Text = "";
            edit2.Visible = false;
            p1.Visible = true;
            Saved.Visible = false;
        }

        /// <summary>
        /// Loads the Selected Page into the Editor for editing
        /// </summary>
        private void themepages_SelectedNodeChanged( object sender, EventArgs e )
        {
            lines.Visible = true;
            p1.Visible = false;
            edit1.Text = "- Editing: " + themepages.SelectedNode.Text;
            edit2.Visible = true;

            wait.Visible = false;
            CancelButton.Visible = true;
            SaveButton.Visible = true;
            var ls = new List< string >();
            string path = Server.MapPath( "~/Themes/" + themepages.SelectedNode.ValuePath.Replace( '\\', '/' ) );
            selectedfile.Value = path;
            var colorizer =
                new Colorizer(
                    Server.MapPath( "~/ControlPanel/Settings/CSLive/Rules.xml" ) );
            using ( TextReader reader = new StreamReader( path ) )
            {
                colorizer.Colorize( "html", reader, ls );
                reader.Close();
                reader.Dispose();
            }
            linerepeater.DataSource = ls.ToArray();
            linerepeater.DataBind();
        }

        /// <summary>
        /// Colorizes a Line
        /// </summary>
        /// <param name="o">A Line of HTML</param>
        /// <returns>A Colorized Line of HTML</returns>
        public string colorize( object o )
        {
            string s = o.ToString();
            var colorizer =
                new Colorizer(
                    Server.MapPath( "~/ControlPanel/Settings/CSLive/Rules.xml" ) );
            return colorizer.ColorizeLine( "html", s );
        }

        /// <summary>
        /// Loads the Available Theme Directories
        /// </summary>
        private void loadThemes()
        {
            foreach ( DirectoryInfo dir in new DirectoryInfo( Server.MapPath( "~/Themes/" ) ).GetDirectories() )
                if ( !dir.Name.ToLower().Equals( ".svn" ) && !dir.Name.ToLower().Equals( "blogs" ) &&
                     !dir.Name.ToLower().Equals( "hubs" ) && !dir.Name.ToLower().Equals( "_svn" ) )
                {
                    var node = new TreeNode( dir.Name, dir.Name );
                    node.NavigateUrl = "javascript:void(0);";
                    populateNode( node, dir );
                    node.CollapseAll();
                    themepages.Nodes.Add( node );
                }
        }

        /// <summary>
        /// Populates the Child Nodes of a root node
        /// </summary>
        /// <param name="node">A Root Node (or theme)</param>
        /// <param name="dir">The Directory of the Theme</param>
        private static void populateNode( TreeNode node, DirectoryInfo dir )
        {
            foreach ( DirectoryInfo d in dir.GetDirectories() )
                if ( !d.Name.ToLower().Equals( ".svn" ) && !d.Name.ToLower().Equals( "_svn" ) )
                {
                    var n = new TreeNode( d.Name, d.Name );
                    n.NavigateUrl = "javascript:void(0);";
                    populateNode( n, d );
                    node.ChildNodes.Add( n );
                }
            foreach ( FileInfo file in dir.GetFiles() )
                if ( file.Extension.ToLower() == ".aspx" || file.Extension.ToLower() == ".ascx" ||
                     file.Extension.ToLower() == ".master" )
                    node.ChildNodes.Add( new TreeNode( file.Name, file.Name ) );
        }
    }
}